﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using WiimoteLib;
using GestureLib;
using System.Drawing;
using System.Windows.Media.Animation;
using System.Timers;
using GestureLib.Implementation;

namespace GestureLibDemo
{
    /// <summary>
    /// Interaction logic for Window1.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private delegate void RecordingFinishedDelegate();

        private GestureLib.GestureLib _gl;
        private SimulationGestureDevice _simulationDevice;
        private MouseGestureDevice _mouseDevice;

        public MainWindow()
        {
            System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");

            InitializeComponent();
        }

        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
            #region Algorithms
            IGestureAlgorithm shakingTopBottom = new ShakingTopBottomGestureAlgorithm();
            IGestureAlgorithm shakingLeftRight = new ShakingLeftRightGestureAlgorithm();
            IGestureAlgorithm leftRight = new LeftRightAccelerationGestureAlgorithm();
            IGestureAlgorithm rightLeft = new RightLeftAccelerationGestureAlgorithm();
            IGestureAlgorithm roll = new RollGestureAlgorithm();
            IGestureAlgorithm jab = new JabGestureAlgorithm();
            IGestureAlgorithm pitch = new PitchGestureAlgorithm();

            IGestureAlgorithm pointerLeftRight = new LeftRightGestureAlgorithm();
            IGestureAlgorithm pointerTopBottom = new TopBottomGestureAlgorithm();
            IGestureAlgorithm pointerBottomTop = new BottomTopGestureAlgorithm();
            IGestureAlgorithm pointerRightLeft = new RightLeftGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalBottomLeftTopRight = new DiagonalBottomLeftTopRightGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalTopLeftBottomRight = new DiagonalTopLeftBottomRightGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalTopRightBottomLeft = new DiagonalTopRightBottomLeftGestureAlgorithm();
            IGestureAlgorithm pointerDiagonalBottomRightTopLeft = new DiagonalBottomRightTopLeftGestureAlgorithm();
            #endregion
            
            #region Actions

            string applicationPath = Utility.GetApplicationPath();
            System.IO.TextWriter debugWriter = new System.IO.StreamWriter(System.IO.Path.Combine(applicationPath, "debug.txt"));
            
            IGestureAction debugStream = new DebugStreamGestureAction()
            {
                DebugMessage = "My Debug-Test",
                WriteNewLine = true,
                Output = debugWriter,
                AutoFlush = true
            };

            IGestureAction fileExecuteCalc = new FileExecuteGestureAction()
            {
                StartInfo = new System.Diagnostics.ProcessStartInfo("calc.exe")
            };

            IGestureAction fileExecuteNotepad = new FileExecuteGestureAction()
            {
                StartInfo = new System.Diagnostics.ProcessStartInfo("notepad.exe")
            };

            IGestureAction shakeWindowHorizontal = new ShakeWindowAnimatedAction()
            {
                MaxLoops = 15,
                Window = this,
                VerticalDifference = 5,
                Direction = ShakeWindowAnimatedAction.ShakeDirection.Horizontal 
            };

            IGestureAction shakeWindowVertical = new ShakeWindowAnimatedAction()
            {
                MaxLoops = 15,
                Window = this,
                VerticalDifference = 5,
                Direction = ShakeWindowAnimatedAction.ShakeDirection.Vertical
            };
            #endregion


            //instanciate GestureLib
            _gl = new GestureLib.GestureLib();

            _gl.Recording.RecordingStarted += new EventHandler(Recording_RecordingStarted);
            _gl.Recording.RecordingFinished += new EventHandler(Recording_RecordingFinished);

           
            #region Add algorithms and actions
            _gl.AvailableGestureAlgorithms.Add(shakingTopBottom);
            _gl.AvailableGestureAlgorithms.Add(shakingLeftRight);
            _gl.AvailableGestureAlgorithms.Add(leftRight);
            _gl.AvailableGestureAlgorithms.Add(rightLeft);
            _gl.AvailableGestureAlgorithms.Add(roll);
            _gl.AvailableGestureAlgorithms.Add(jab);
            _gl.AvailableGestureAlgorithms.Add(pitch);

            _gl.AvailableGestureAlgorithms.Add(pointerLeftRight);
            _gl.AvailableGestureAlgorithms.Add(pointerTopBottom);
            _gl.AvailableGestureAlgorithms.Add(pointerBottomTop);
            _gl.AvailableGestureAlgorithms.Add(pointerRightLeft);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalBottomLeftTopRight);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalTopLeftBottomRight);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalBottomRightTopLeft);
            _gl.AvailableGestureAlgorithms.Add(pointerDiagonalTopRightBottomLeft);

            _gl.AvailableGestureActions.Add(debugStream);
            _gl.AvailableGestureActions.Add(fileExecuteCalc);
            _gl.AvailableGestureActions.Add(fileExecuteNotepad);
            _gl.AvailableGestureActions.Add(shakeWindowHorizontal);
            _gl.AvailableGestureActions.Add(shakeWindowVertical);
            #endregion

            #region Add trained Gestures
            TrainedGesture topDownGesture = new TrainedGesture();
            topDownGesture.GestureActions.Add(shakeWindowVertical);
            topDownGesture.GestureAlgorithms.Add(shakingTopBottom);
            topDownGesture.Name = "TopDownGesture";

            _gl.TrainedGestures.Add(topDownGesture);

            TrainedGesture leftRightGesture = new TrainedGesture();
            leftRightGesture.GestureActions.Add(shakeWindowHorizontal);
            leftRightGesture.GestureAlgorithms.Add(shakingLeftRight);
            leftRightGesture.Name = "LeftRightGesture";

            _gl.TrainedGestures.Add(leftRightGesture);
            /*
            TrainedGesture leftRightGesture = new TrainedGesture();
            leftRightGesture.GestureActions.Add(fileExecuteNotepad);
            leftRightGesture.GestureActions.Add(fileExecuteCalc);
            leftRightGesture.GestureAlgorithms.Add(shakingLeftRight);
            leftRightGesture.Name = "LeftRightTest";

            _gl.TrainedGestures.Add(leftRightGesture);*/
            
            /*TrainedGesture topDownPointerGesture = new TrainedGesture();
            topDownPointerGesture.GestureActions.Add(shakeWindow);
            topDownPointerGesture.GestureAlgorithms.Add(pointerTopBottom);
            topDownPointerGesture.GestureAlgorithms.Add(pointerBottomTop);
            topDownPointerGesture.Name = "TopBottomBottomTop";

            _gl.TrainedGestures.Add(topDownPointerGesture);*/
            #endregion

            #region Setup Device
            //_simulationDevice = CreateSimulationDevice();
            
            //_mouseDevice = CreateMouseDevice();
            //_gl.GestureDevice = _mouseDevice;

            //_gl.GestureDevice = CreateWiiAirMouseGestureDevice();
            _gl.GestureDevice = CreateWiiDevice();
            //_gl.GestureDevice = CreateTcpServerGestureDevice();
            #endregion 

            #region Common GestureLib Options
            //impersonating the mouse
            _gl.ScreenBounds = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
            _gl.ImpersonatingMouse = true;

            //set recording style
            _gl.Recording.EventFilterNumber = 1;
            _gl.Recording.ShowOverlayWhenBeginRecording = true;
            #endregion

            lblUsedDevice.Content = _gl.GestureDevice.GetType().ToString();
        }

        private void RecordingFinished()
        {
            if (this.Dispatcher.CheckAccess())
            {
                lock (this)
                {
                    System.Diagnostics.Debug.WriteLine("GestureRecording finished");

                    lbPointerGestureStates.ItemsSource = _gl.Recording.RecordedPointerGestureStates;
                    lbAccelerationGestureStates.ItemsSource = _gl.Recording.RecordedAccelerationGestureStates;

                    lblPointerCount.Content = _gl.Recording.RecordedPointerGestureStates.Count;
                    lblAccelerationCount.Content = _gl.Recording.RecordedAccelerationGestureStates.Count;

                    cvsPath.Children.Clear();

                    double cvsHeight = cvsPath.ActualHeight;
                    double cvsTop = cvsPath.ActualWidth;

                    foreach (PointerGestureState pgs in _gl.Recording.RecordedPointerGestureStates)
                    {
                        Ellipse el = new Ellipse();

                        SolidColorBrush bgColor = new SolidColorBrush();
                        bgColor.Color = Colors.Red;

                        el.Width = 5;
                        el.Height = 5;
                        el.Fill = bgColor;
                        
                        el.SetValue(Canvas.LeftProperty, (double)pgs.X * cvsTop);
                        el.SetValue(Canvas.TopProperty, (double)pgs.Y * cvsHeight);

                        cvsPath.Children.Add(el);
                    }


                    PointTendenceAnalyzer pointTendenceAnalyzer = _gl.Recording.RecognizeRecording(_gl.Recording.RecordedPointerGestureStates);
                    GestureAlgorithmCollection matchedPointerAlgorithmes = pointTendenceAnalyzer.MatchedGestureAlgorithms;
                    lbMatchedPointerAlgorithms.ItemsSource = matchedPointerAlgorithmes;

                    #region Draw on canvas (if recognized lines exist)
                    if (pointTendenceAnalyzer.CornerMarks != null)
                    {
                        if (pointTendenceAnalyzer.CornerMarks.Count > 0)
                        {
                            for (int i = 1; i < pointTendenceAnalyzer.CornerMarks.Count; i++)
                            {
                                PointF startPoint = pointTendenceAnalyzer.CornerMarks[i - 1];
                                PointF endPoint = pointTendenceAnalyzer.CornerMarks[i];

                                //////////////////////////////

                                Label lbl = new Label();
                                lbl.Content = i.ToString();

                                lbl.FontSize = 15;

                                lbl.SetValue(Canvas.LeftProperty, (double)startPoint.X * cvsTop - 20.0);
                                lbl.SetValue(Canvas.TopProperty, (double)startPoint.Y * cvsHeight - 20.0);

                                cvsPath.Children.Add(lbl);
                                //////////////////////////////

                                Line line = new Line();

                                line.Stroke = System.Windows.Media.Brushes.Green;
                                line.StrokeThickness = 2;

                                line.X1 = (double)startPoint.X * cvsTop;
                                line.Y1 = (double)startPoint.Y * cvsHeight;
                                line.X2 = (double)endPoint.X * cvsTop;
                                line.Y2 = (double)endPoint.Y * cvsHeight;

                                cvsPath.Children.Add(line);

                                //////////////////////////////

                                Ellipse elStart = new Ellipse();

                                elStart.Width = 5;
                                elStart.Height = 5;
                                elStart.Fill = System.Windows.Media.Brushes.Green;

                                elStart.SetValue(Canvas.LeftProperty, (double)startPoint.X * cvsTop);
                                elStart.SetValue(Canvas.TopProperty, (double)startPoint.Y * cvsHeight);

                                cvsPath.Children.Add(elStart);

                                //////////////////////////////

                                Ellipse elEnd = new Ellipse();

                                elEnd.Width = 5;
                                elEnd.Height = 5;
                                elEnd.Fill = System.Windows.Media.Brushes.Green;

                                elEnd.SetValue(Canvas.LeftProperty, (double)endPoint.X * cvsTop);
                                elEnd.SetValue(Canvas.TopProperty, (double)endPoint.Y * cvsHeight);

                                cvsPath.Children.Add(elEnd);
                            }
                        }
                    }
                    #endregion

                    TrainedGesture trainedGesture = _gl.TrainedGestures.GetTrainedGestureByMatchedAlgorithms(matchedPointerAlgorithmes);
                    if (trainedGesture != null)
                    {
                        trainedGesture.GestureActions.ForEach(ga => ga.Execute());
                    }

                    ////////////////////////////////////////////////////////////////////////

                    GestureAlgorithmCollection matchedAccelerationAlgorithmes = _gl.Recording.RecognizeRecording(_gl.Recording.RecordedAccelerationGestureStates);
                    lbMatchedAlgorithms.ItemsSource = matchedAccelerationAlgorithmes;

                    trainedGesture = _gl.TrainedGestures.GetTrainedGestureByMatchedAlgorithms(matchedAccelerationAlgorithmes);
                    if (trainedGesture != null)
                    {
                        trainedGesture.GestureActions.ForEach(ga => ga.Execute());
                    }
                }
            }
            else
            {
                this.Dispatcher.Invoke(
                    System.Windows.Threading.DispatcherPriority.Normal,
                    new RecordingFinishedDelegate(RecordingFinished));
            }
        }

        void Recording_RecordingFinished(object sender, EventArgs e)
        {
            RecordingFinished();
        }

        void Recording_RecordingStarted(object sender, EventArgs e)
        {
            System.Diagnostics.Debug.WriteLine("GestureRecording started");
        }

        private void btnSimulate_Click(object sender, RoutedEventArgs e)
        {
            _simulationDevice.StartGestureDevice();
        }

        private SimulationGestureDevice CreateSimulationDevice()
        {
            SimulationGestureDevice simulationDevice;

            string applicationPath = Utility.GetApplicationPath();

            simulationDevice = new SimulationGestureDevice();
            simulationDevice.Speed = 10;
            simulationDevice.SourceFileName = System.IO.Path.Combine(
                applicationPath,
                "SimulationData.csv");

            btnSimulate.IsEnabled = false;

            return simulationDevice;
        }

        private WiiGestureDevice CreateWiiDevice()
        {
            Wiimote wiimote = new Wiimote();
            wiimote.Connect();
            wiimote.SetReportType(Wiimote.InputReport.IRAccel, true);
            wiimote.SetLEDs(false, true, true, false);

            //attach create WiiGestureDevice to GestureLib
            WiiGestureDevice wiiDevice = new WiiGestureDevice();
            wiiDevice.Dispatcher = Application.Current.Dispatcher;
            wiiDevice.Wiimote = wiimote;
                        
            return wiiDevice;
        }

        private MouseGestureDevice CreateMouseDevice()
        {
            MouseGestureDevice mouseDevice = new MouseGestureDevice(
                new System.Drawing.Size(
                    (int)System.Windows.SystemParameters.PrimaryScreenWidth, 
                    (int)System.Windows.SystemParameters.PrimaryScreenHeight),
                true);

            return mouseDevice;
        }

        private TcpServerGestureDevice CreateTcpServerGestureDevice()
        {
            TcpServerGestureDevice tcpServerGestureDevice = new TcpServerGestureDevice(
                new System.Drawing.Size(
                    (int)System.Windows.SystemParameters.PrimaryScreenWidth,
                    (int)System.Windows.SystemParameters.PrimaryScreenHeight));

            tcpServerGestureDevice.Dispatcher = Application.Current.Dispatcher;

            tcpServerGestureDevice.StartListening();
            
            return tcpServerGestureDevice;
        }

        private WiiAirMouseGestureDevice CreateWiiAirMouseGestureDevice()
        {
            Wiimote wiimote = new Wiimote();
            wiimote.Connect();
            wiimote.SetReportType(Wiimote.InputReport.IRAccel, true);
            wiimote.SetLEDs(false, true, true, false);

            WiiAirMouseGestureDevice wiiAirMouseGestureDevice = new WiiAirMouseGestureDevice(
                new System.Drawing.Size(
                    (int)System.Windows.SystemParameters.PrimaryScreenWidth,
                    (int)System.Windows.SystemParameters.PrimaryScreenHeight));

            wiiAirMouseGestureDevice.Dispatcher = Application.Current.Dispatcher;
            wiiAirMouseGestureDevice.Wiimote = wiimote;

            return wiiAirMouseGestureDevice;
        }

        private void btnCopyAccelerationGestureStates_Click(object sender, RoutedEventArgs e)
        {
            if (_gl.Recording.RecordedAccelerationGestureStates.Count > 0)
            {
                string clipboardText = string.Empty;

                _gl.Recording.RecordedAccelerationGestureStates.ForEach(
                    delegate(AccelerationGestureState ags)
                    {
                        clipboardText += string.Format(System.Globalization.CultureInfo.GetCultureInfo("de-DE"), "{0}\t{1}\t{2}\n", ags.X, ags.Y, ags.Z);
                    });

                Clipboard.SetText(clipboardText);
            }
        }

        private void btnCopyPointerGestureStates_Click(object sender, RoutedEventArgs e)
        {
            if (_gl.Recording.RecordedPointerGestureStates.Count > 0)
            {
                string clipboardText = string.Empty;

                _gl.Recording.RecordedPointerGestureStates.ForEach(
                    delegate(PointerGestureState pgs)
                    {
                        clipboardText += string.Format(System.Globalization.CultureInfo.GetCultureInfo("de-DE"), "{0}\t{1}\n", pgs.X, pgs.Y);
                    });

                Clipboard.SetText(clipboardText);
            }
        }
    }
}
